﻿using Microsoft.EntityFrameworkCore;
using ProxyServer.EntityFrameworkCore;
using ProxyServer.Extensions;
using Serilog;
using System.CommandLine;

namespace ProxyServer.CommandLine.Commands;

public class DatabaseCommand : Command
{
    public static DatabaseCommand Instance = new DatabaseCommand();

    private DatabaseCommand() : base("database", "")
    {
        AddCommand(UpdateCommand.Instance);
        AddCommand(StatusCommand.Instance);
    }

    public class StatusCommand : Command
    {
        public static StatusCommand Instance = new StatusCommand();

        private StatusCommand() : base("status", "Show the current migration status of the database.")
        {
            var args = Global.BuildRemainArgs();
            AddArgument(args);

            this.SetHandler(async (ctx) =>
            {
                try
                {
                    string[] remaining_args = ctx.ParseResult.GetValueForArgument(args);

                    var factory = DesignTimeFactoryBuilder.Build(remaining_args);
                    var databaseProvider = factory.Configuration.GetDatabaseProvider();

                    using var dbContext = factory.CreateDbContext([]);

                    var migrations = await dbContext.Database.GetAppliedMigrationsAsync(ctx.GetCancellationToken()).ConfigureAwait(false);

                    Console.WriteLine($"({databaseProvider}) Applied migrations:");
                    foreach (var migration in migrations)
                    {
                        Console.WriteLine(migration);
                    }

                    var pending = await dbContext.Database.GetPendingMigrationsAsync(ctx.GetCancellationToken()).ConfigureAwait(false);

                    if (pending.IsNullOrEmpty() == false)
                    {
                        Console.WriteLine($"({databaseProvider}) Pending migrations:");
                        foreach (var migration in pending)
                        {
                            Console.WriteLine(migration);
                        }
                    }
                }
                catch (System.Exception ex)
                {
                    ctx.ExitCode = 1;
                    Log.Logger.Error(ex, "Failed to get database status.");
                }
            });
        }
    }

    public class UpdateCommand : Command
    {
        static Option<string> TargetOption = new Option<string>(
            "--target",
            "The target migration to apply to. If not provided, the latest migration will be applied."
        );

        public static UpdateCommand Instance = new UpdateCommand();

        private UpdateCommand() : base("update", "Update the database with the latest migration.")
        {
            var args = Global.BuildRemainArgs();

            AddArgument(args);
            AddOption(TargetOption);

            this.SetHandler(async (ctx) =>
            {
                try
                {
                    var target = ctx.ParseResult.GetValueForOption(TargetOption);
                    var remaining_args = ctx.ParseResult.GetValueForArgument(args);

                    var factory = DesignTimeFactoryBuilder.Build(remaining_args);
                    var databaseProvider = factory.Configuration.GetDatabaseProvider();

                    using (var db = factory.CreateDbContext([]))
                    {
                        var pending = await db.Database.GetPendingMigrationsAsync(ctx.GetCancellationToken()).ConfigureAwait(false);
                        if (pending.IsNullOrEmpty())
                        {
                            Console.WriteLine($"({databaseProvider}) No pending migrations to apply.");
                            return;
                        }

                        foreach (var migration in pending)
                        {
                            Console.WriteLine($"Applying migration: {migration}");
                        }
                    }

                    using var dbContext = factory.CreateDbContext([]);
                    await dbContext.Database.MigrateAsync(target, ctx.GetCancellationToken()).ConfigureAwait(false);

                    Console.WriteLine($"({databaseProvider}) Database updated successfully to target: {target ?? "latest"}");
                }
                catch (System.Exception ex)
                {
                    ctx.ExitCode = 1;
                    Log.Logger.Error(ex, "Failed to get database status.");
                }
            });
        }
    }
}
